﻿namespace NTLib.Core.Threading
{
    using NTLib.Core.Memory;
    using System;
    using System.Threading.Tasks;
    using System.Threading;
    using System.Runtime.CompilerServices;

    /// <summary>
    /// Locker implementation using a ReaderWriterSlimMutex
    /// </summary>
    public sealed class ReaderWriterLocker : Disposable, ILocker
    {
        #region Fields

        private readonly ReaderWriterLockSlim _locker;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="ReaderWriterLocker"/> class.
        /// </summary>
        public ReaderWriterLocker()
        {
            this._locker = new ReaderWriterLockSlim();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ReaderWriterLocker"/> class.
        /// </summary>
        /// <param name="locker">The locker.</param>
        public ReaderWriterLocker(ReaderWriterLockSlim locker = null)
        {
            this._locker = locker;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Lock(bool read = false)
        {
            base.CheckDisposed();
            if (read)
                this._locker.EnterReadLock();
            else
                this._locker.EnterWriteLock();
        }

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public IDisposable ScopeLock(bool read = false)
        {
            base.CheckDisposed();
            if (read)
                return this._locker.LockRead();
            else
                return this._locker.LockWrite();
        }

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public Task LockAsync(bool read = false, CancellationToken token = default(CancellationToken))
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Scopes the lock asynchronous.
        /// </summary>
        /// <param name="read">if set to <c>true</c> [read].</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        /// <exception cref="System.NotImplementedException"></exception>
        public Task<IDisposable> ScopeLockAsync(bool read = false, CancellationToken token = default(CancellationToken))
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Releases the specified access.
        /// </summary>
        /// <param name="fromRead">if set to <c>true</c> the acces will be lock from read depend of the locker item used.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Release(bool fromRead = false)
        {
            base.CheckDisposed();
            if (fromRead)
                this._locker.ExitReadLock();
            else
                this._locker.ExitWriteLock();
        }

        /// <summary>
        /// Dispose the managed objects.
        /// </summary>
        protected override void ManagedDispose()
        {
            this._locker.Dispose();
            base.ManagedDispose();
        }

        #endregion
    }
}
